package homework.server.terminal;

import homework.server.IOStream;
import homework.server.terminal.impl.LoginCommand;

import java.io.Closeable;
import java.io.IOException;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import java.util.MissingFormatArgumentException;

public class Terminal implements Closeable {
    public static final int CMD_CONTINUE_CODE = 0xc0;
    public static final int CMD_NOT_FOUND_CODE = -1;
    public static final int CMD_RAISED_CODE = -2;
    public static final String defaultErrorFormat = "Error:%s:%s";

    private Map<String, String> globalEnv;
    private Map<String, String> localEnv;
    private Map<String, Command> cmds;

    private IOStream stream;

    private boolean isClosed = false;

    private Command currentCmd = new LoginCommand();

    public Terminal() {
        this(new IOStream(System.in, System.out));
    }

    public Terminal(IOStream ioStream) {
        this(ioStream, new HashMap<String, String>());
    }

    public Terminal(IOStream ioStream, HashMap<String, String> global) {
        this(ioStream, global, new HashMap<String, Command>());
    }

    public Terminal(IOStream ioStream, Map<String, String> global, Map<String, Command> execMap) {
        stream = ioStream;
        globalEnv = global;
        localEnv = new HashMap<String, String>();
        cmds = execMap;

        global.putIfAbsent("LANG", Charset.defaultCharset().name());
    }

    public void setCommand(String key, Command executable) {
        cmds.put(key, executable);
    }

    public String getVariable(String key) {
        String value = localEnv.get(key);
        return value == null ? globalEnv.get(key) : value;
    }

    public void setVariable(String key, String value) {
        localEnv.put(key, value);
    }

    public String deleteVariable(String key) {
        return localEnv.remove(key);
    }

    public int execute(String cmd, String[] args) throws IOException {
        // XXX: args使用String传入, 调用parseArgs方法分割
        stream.setCharset(getVariable("LANG"));
        int resultCode;
        if(currentCmd == null)
            currentCmd = cmds.get(cmd);

        if(currentCmd == null) {
            stream.writeLine("[ERROR] 没有此命令:"+cmd);
            resultCode = CMD_NOT_FOUND_CODE;
        } else {
            try {
                resultCode = currentCmd.execute(args, this);
            } catch (Exception e) {
                sendError(e, e.getMessage());
                resultCode = CMD_RAISED_CODE;
            }

            if(resultCode != CMD_CONTINUE_CODE)
                currentCmd = null;
        }

        return resultCode;
    }

    public String[] parseArgs(String argsStr) throws ParseException {
        // TODO: 参数字符串解析分割
        return null;
    }

    private static String getClassName(Object obj) {
        String[] parts = obj.getClass().toString().split("\\.|\\s+");
        if(parts.length == 0) return obj.getClass().toString();
        return parts[parts.length-1];
    }

    public String sendError(Exception e, String msg) throws IOException {
        String format = getVariable("ERROR_FORMAT");
        if(format == null) format = defaultErrorFormat;

        String errName = getClassName(e);
        String errMsg;
        try {
            errMsg = String.format(format, errName, msg);
        } catch (MissingFormatArgumentException formatErr) {
            errMsg = String.format(defaultErrorFormat, errName, msg);
        }
        stream.writeLine(errMsg);
        return errMsg;
    }

    public void sendMessage(String msg) throws IOException {
        stream.writeLine(msg);
    }

    public boolean isClosed() {
        return isClosed;
    }

    @Override
    public void close() throws IOException {
        isClosed = true;
    }

    public IOStream getIOStream() {
        return stream;
    }
}
